package calao.util;

import java.io.PrintWriter;

/** Low-level logger.
 * This logger is designed to be simple and resource-care. The use of formatted
 * messages with arguments eliminates string buffer operations for non-logged 
 * records. 
 * <p>
 * The logger has a default console handler.
 */
public class Logger 
{
	
	/** Identifies error-related log records.
	 */
	public static LogLevel ERROR = new LogLevel(400, "ERR");
	
	/** Identifies log records that may relate to errors.
	 */
	public static LogLevel WARNING = new LogLevel(300, "WAR");

	/** Identifies informative messages related to normal system execution
	 * or configuration.
	 */ 
	public static LogLevel INFO = new LogLevel(200, "INF");

	/** Identifies debugging information.
	 */
	public static LogLevel DEBUG = new LogLevel(100, "DEB");
	
	static ConsoleHandler defaultHandler = new ConsoleHandler();

	static LogHandler[] handlers = new LogHandler[] { defaultHandler };
	
	/** Logs an exception.
	 */
	public static void log(final Throwable throwable)
	{
		LogRecord record = new LogRecord(ERROR) {
			@Override
			public void writeMessage(PrintWriter writer) {
				throwable.printStackTrace(writer);
			}
		};
		log(record);
	}

	/** Logs a formatted message.
	 * 
	 * @param level the log level of the message.
	 * @param msg the message to log (see {@link java.util.Formatter format details}
	 * @param args the message arguments
	 */
	public static void log(LogLevel level, final String msg, final Object... args)
	{
		LogRecord record = new LogRecord(level) {
			@Override
			public void writeMessage(PrintWriter writer) {
				try {
					writer.printf(msg, args);
					writer.println();
				}
				catch (Exception e) {
					writer.print("... (Format exception: ");
					writer.print(e.getMessage());
					writer.println(")");
				}
			}
		};
		log(record);
	}
	
	static void log(LogRecord record)
	{
		for (LogHandler handler : handlers) record.handleWith(handler);
	}
		
	/** Binds a new log record handler.
	 */
	public static synchronized void bind(LogHandler handler)
	{
		for (LogHandler chandler : handlers) {
			if (chandler==handler) return;
		}
		// NB ff operation allows other threads to work on handlers asynchronously
		handlers = Arrays.append(handlers, handler);
	}
	
	/** Releases a log handler.
	 */ 
	public static synchronized void release(LogHandler handler)
	{
		if (handler==defaultHandler) throw new IllegalArgumentException();
		// NB ff operation allows other threads to work on handlers asynchronously
		handlers = Arrays.remove(handlers, handler);
	}
	
	public static ConsoleHandler getDefaultHandler()
	{
		return defaultHandler;
	}

}
